Plugin format for skills + local NuGet flow + agent kit#225
Conversation
Restructures the agent-facing skills around the Copilot CLI / Claude
plugin layout (plugins/reactor/{plugin.json, agents/, skills/<name>/SKILL.md})
so the SDK's native skill-loading subsystem fires `skill.invoked` instead
of the agent treating SKILL.md as an ordinary file. Each topical skill
gets a focused SKILL.md with keyword-rich frontmatter; deeper content
lives under references/.
New skills (under plugins/reactor/skills/):
- reactor-getting-started — minimal single-file `#:package` starter, .csproj
template, mode detection, package consumption, cache map fallback
- reactor-dsl — DSL essentials, hooks, components, theme tokens, gotchas;
references/reactor.api.txt is the full alphabetized signatures index
- reactor-build-and-check — `mur check` usage, common-build-errors cheat
table mapping REACTOR_* / CS* IDs to one-line fixes
- reactor-{async,design,forms,navigation,input,charts,commanding,devtools}
— migrated from skills/<name>.md with plugin-format frontmatter
- reactor-recipes — paste-ready single-file recipes in references/
Local NuGet flow + agent kit:
- tools/Reactor.SignaturesGen — reflection-based generator emitting
skills/reactor.api.txt from the built Reactor.dll. AfterBuild target
skips when RuntimeIdentifier is set so cross-arch publishes don't fail.
- nuget.config + local-nupkgs/ — repo-level local feed
- mur pack-local — packs Microsoft.UI.Reactor.0.0.0-local.nupkg into the
feed and clears NuGet's HTTP cache
- mur api / --regen-api — print / regenerate the signatures index
- mur check — runs `dotnet build` and emits one-line diagnostics with
skill-file pointers for known REACTOR_* analyzer IDs
- Reactor.csproj packs the full plugin tree into agentkit/plugins/reactor/
alongside the legacy SKILL.md + skills/ + reactor.api.txt
New analyzer:
- REACTOR_DSL_001 (MissingWithKeyAnalyzer) — flags Select(...) into a
layout container without .WithKey on items
Recipes (skills/recipes/):
- async-fetch-list, form-with-validation, list-add-delete, sidebar-nav,
themed-card — all compile against the local feed; verified end-to-end
via c:\temp\nuget-test*.
Root SKILL.md is preserved as a fallback for environments without plugin
loading and now leads with a pointer to the plugin path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR reorganizes Reactor’s agent-facing documentation into a plugin-based skill layout, adds a local NuGet packing workflow for selfhost development, and introduces tooling/analyzers to reduce agent token usage and improve fast diagnostics.
Changes:
- Add
plugins/reactor/plugin format (agents + per-skill docs + recipe references) and pack the full “agent kit” into theMicrosoft.UI.ReactorNuGet underagentkit/. - Introduce
mur pack-localand repo-levelnuget.configto enable a consistent local-feed workflow (0.0.0-local). - Add
mur checkfor one-line diagnostics + a new analyzerREACTOR_DSL_001to detect missing.WithKey(...)in common dynamic-list patterns, plus a signatures generator (reactor.api.txt).
Show a summary per file
| File | Description |
|---|---|
| tools/Reactor.SignaturesGen/Reactor.SignaturesGen.csproj | New tool project to reflect over Reactor and emit the API signatures index. |
| tools/Reactor.SignaturesGen/Program.cs | Implements API index generation (factories/modifiers/hooks/theme/enums). |
| src/Reactor/Reactor.csproj | Packs agent kit (legacy skills + plugin tree + signatures index) into the Reactor NuGet. |
| src/Reactor.Cli/Reactor.Cli.csproj | Embeds reactor.api.txt into mur and adds build-order dependency on signatures generator. |
| src/Reactor.Cli/Program.cs | Adds --api, --regen-api, check, and pack-local CLI entry points. |
| src/Reactor.Cli/Pack/PackLocalCommand.cs | Implements mur pack-local to pack a local 0.0.0-local nupkg into local-nupkgs/. |
| src/Reactor.Cli/Check/CheckCommand.cs | Implements mur check wrapper around dotnet build with structured one-line diagnostics + hints. |
| src/Reactor.Analyzers/MissingWithKeyAnalyzer.cs | Adds REACTOR_DSL_001 analyzer for missing .WithKey(...) in dynamic list projections. |
| src/Reactor.Analyzers/AnalyzerReleases.Unshipped.md | Registers the new analyzer ID in release notes. |
| skills/recipes/themed-card.cs | New legacy recipe demonstrating themed card surface. |
| skills/recipes/sidebar-nav.cs | New legacy recipe demonstrating NavigationView + UseNavigation. |
| skills/recipes/list-add-delete.cs | New legacy recipe demonstrating keyed dynamic list + reducer updates. |
| skills/recipes/index.md | New legacy recipes index (intent → recipe map). |
| skills/recipes/form-with-validation.cs | New legacy recipe for validation context + FormField. |
| skills/recipes/async-fetch-list.cs | New legacy recipe for UseResource async loading/data/error states. |
| skills/reactor.api.txt | Generated legacy signatures index committed to repo. |
| SKILL.md | Updates legacy monolithic skill to point to plugin format + local NuGet flow + new CLI commands. |
| Reactor.slnx | Adds the new signatures generator tool project to the solution. |
| plugins/reactor/plugin.json | Adds plugin manifest for plugin-based skill loading. |
| plugins/reactor/.claude-plugin/plugin.json | Adds Claude plugin manifest for the same plugin content. |
| plugins/reactor/agents/reactor-dev.agent.md | Defines reactor-dev agent instructions and skill-loading guidance. |
| plugins/reactor/skills/reactor-getting-started/SKILL.md | Plugin skill: setup/bootstrap + selfhost vs consumer guidance. |
| plugins/reactor/skills/reactor-dsl/SKILL.md | Plugin skill: DSL essentials + gotchas + pointer to signatures index. |
| plugins/reactor/skills/reactor-dsl/references/reactor.api.txt | Plugin copy of the generated signatures index for efficient lookup. |
| plugins/reactor/skills/reactor-build-and-check/SKILL.md | Plugin skill: build/check workflow + common diagnostics cheat table. |
| plugins/reactor/skills/reactor-async/SKILL.md | Plugin skill: async hooks (UseResource/UseMutation/UseInfiniteResource). |
| plugins/reactor/skills/reactor-forms/SKILL.md | Plugin skill: controlled inputs + validation + FormField patterns. |
| plugins/reactor/skills/reactor-navigation/SKILL.md | Plugin skill: navigation model and common patterns (sidebar, tabs, deep links). |
| plugins/reactor/skills/reactor-input/SKILL.md | Plugin skill: pointer/gesture APIs, focus management, drag/drop. |
| plugins/reactor/skills/reactor-charts/SKILL.md | Plugin skill: charting guidance and DSL usage. |
| plugins/reactor/skills/reactor-commanding/SKILL.md | Plugin skill: commands/accelerators/UseCommand guidance. |
| plugins/reactor/skills/reactor-devtools/SKILL.md | Plugin skill: mur devtools usage and workflows. |
| plugins/reactor/skills/reactor-recipes/SKILL.md | Plugin skill: recipe index and usage contract. |
| plugins/reactor/skills/reactor-recipes/references/themed-card.cs | Plugin recipe reference: themed card. |
| plugins/reactor/skills/reactor-recipes/references/sidebar-nav.cs | Plugin recipe reference: sidebar navigation. |
| plugins/reactor/skills/reactor-recipes/references/list-add-delete.cs | Plugin recipe reference: list add/toggle/delete with keys. |
| plugins/reactor/skills/reactor-recipes/references/index.md | Plugin recipes index. |
| plugins/reactor/skills/reactor-recipes/references/form-with-validation.cs | Plugin recipe reference: form validation. |
| plugins/reactor/skills/reactor-recipes/references/async-fetch-list.cs | Plugin recipe reference: async fetch list. |
| nuget.config | Adds repo-level local feed source (local-nupkgs) plus nuget.org. |
| local-nupkgs/.gitignore | Prevents committing locally packed nupkgs/snupkgs. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 42/42 changed files
- Comments generated: 7
CI was failing on `dotnet build Reactor.slnx` (windows-latest, x64 host)
because the slnx defaults Platform=ARM64 for projects that opt in. The
AfterBuild target then runs `Reactor.SignaturesGen.exe` — an ARM64 apphost
the x64 host cannot execute, exiting with code 216 ("not compatible with
the version of Windows you're running") and producing MSB3073.
The previous skip-condition only checked RuntimeIdentifier; this path
sets only Platform. Now resolve target arch from Platform/RID and host
arch from \$(NETCoreSdkRuntimeIdentifier), and skip regen when they differ.
The committed skills/reactor.api.txt is still picked up by pack.
Verified locally on win-arm64:
-p:Platform=ARM64 → regen runs (target=arm64, host=arm64)
-p:Platform=x64 → regen skipped (target=x64, host=arm64)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reframe the bootstrap step as imperatives ("Load the skill") rather than
"these two skills" so the agent treats them as actions. Make the
fallback path explicit (mur --skill / --api / cache-map reads) with
the cost rationale spelled out.
- mur check: drain stdout/stderr concurrently to avoid pipe-buffer deadlock on noisy `dotnet build` output - MissingWithKeyAnalyzer: fix layout-factory list — `WrapPanel` is not a Reactor factory; use `WrapGrid`. Drop `ScrollView` (single-child, not Element[]). - Recipes index (both legacy and plugin copies): wording said `#:project ../../src/Reactor` but the recipes use `#:package Microsoft.UI.Reactor@0.0.0-local`. Align to the local-feed flow. - reactor-recipes skill / recipe indices: drop `Pending` from the async-fetch row — the recipe uses `UseResource` + `AsyncValue<T>.Match` and never references `Pending`. - mur --regen-api: pass `-p:Platform=<host arch>` to match check / pack-local; WinUI projects require an explicit Platform. - SignaturesGen: write to both `skills/reactor.api.txt` (legacy / agentkit) and `plugins/reactor/skills/reactor-dsl/references/ reactor.api.txt` (plugin path). Single source of truth — the two committed copies stay in sync. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`dotnet new reactorapp` requires both the framework nupkg and the project-templates nupkg. Previously only the framework was packed; the templates package was emitted only to tools/Templates/bin/Release/ as a side effect of GeneratePackageOnBuild, leaving evals and dev workflows to discover an undocumented path. Now `mur pack-local` produces both packages in <repo>/local-nupkgs/ and prints the dotnet-new install + create commands at the end. Includes a note that consumers outside the clone need an absolute-path nuget.config or a global feed registration, since the in-repo nuget.config uses a relative path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move the 90% DSL content into reactor-getting-started so most tasks need a single skill load. Slim reactor-dsl to a pointer at the full references/reactor.api.txt index (loaded only when a less-common signature needs verification). Update reactor-dev process guidance to emphasize batch edits, a final build (not per-file rebuilds), and React-shaped intuition with C# spelling.
Eval results — first iteration of 5x batch on this branchModel:
|
| Phase 1 naive (in-cwd skill.md, gpt-5.5) | Phase 2 5-mean (skill loaded as a file) | this branch + dotnet new reactorapp |
Δ vs Phase 1 | Δ vs Phase 2 | |
|---|---|---|---|---|---|
| wall | 518 s | 290.8 s | 197 s | −62% | −32% |
| turns | 17 | 15.6 | 11 | −35% | −29% |
| total tokens | 661,259 | 497,867 | 390,276 | −41% | −22% |
| cost (USD) | $5.10 | $4.68 | $3.30 | −35% | −29% |
| first build | (n/a) | (n/a) | ok ✓ |
reactor-kanban — gap to WinUI XAML
The kanban scenario was added after Phase 5; the closest pre-template baseline I have is the post-plugin / pre-dotnet new reactorapp run from earlier today. Tokens are model-independent so they're the cleanest comparison; wall time is mixed model+skill effect.
| pre-template baseline | this branch + template + React-anchor + pointer-table | Δ | |
|---|---|---|---|
| wall | 1131 s (sonnet-4.6) | 430 s (gpt-5.5) | mixed |
| turns | 21 | 15 | −29% |
| total tokens | 1,582,023 | 756,208 | −52% |
| tools / turn | 1.10 | 2.40 | (batching directive working) |
| first build | ok | ok ✓ |
Side-by-side with winui-xaml-kanban (same harness, same model)
| reactor-kanban | winui-xaml-kanban | reactor / winui | |
|---|---|---|---|
| wall | 430 s | 495 s | 0.87× ✓ reactor faster |
| turns | 15 | 13 | 1.15× |
| tools / turn | 2.40 | 2.00 | reactor batches tighter |
| total tokens | 756,208 | 556,073 | 1.36× |
| cost (USD) | $4.50 | $3.90 | 1.15× |
| LoC (agent) | 305 | 598 | 0.51× (DSL is denser) |
The pre-PR ratio was 1.86× (reactor / winui tokens) on the kanban scenario. After this branch's changes, it's 1.36× — that closes ~58% of the gap.
What moved the numbers (in order of impact, from event-log diagnostics)
dotnet new reactorapptemplate + the local-feed setup wiring (this PR'sd328411). The single biggest structural lever — replaces ~7 file-create turns with onepowershellinvocation. This alone took the post-pluginreactor-kanbanrun from 504 s → 270 s in isolation.- Plugin-format skills (
e983eec). Eliminates the +30 K mid-session conversation injection that pre-plugin runs paid back across 16+ subsequent turns. Confirmed on event traces: pre-plugin step-6 conversation jumps +32 K; post-plugin it's ~+5 K. reactor-getting-startedrewrite (4b51cea+ follow-on). Moved the React→Reactor mapping to the top, absorbed hooks/factories/gotchas, slimmedreactor-dslto a pointer atreferences/reactor.api.txt.- Agent.md edit. Directs the agent to scaffold via
dotnet new reactorappfirst, then batch its file-creates and build at the end (not after every file). The "build at the end" line lifted tools/turn from 1.10 to 1.55–2.40 across runs.
Things tried that didn't pay off
- Inline paste-ready code samples (drag/drop, ContentDialog, flyout, context provider) embedded in the skill regressed turns by ~10% and tokens by ~4%. The agent saw the samples as a feature menu and generated more code. Replaced with a pointer table ("for X see
samples/.../Y.cs") at ~1/30th the always-loaded token cost — that recovered the regression.
Caveats
- This is iter 1 of 5. Reactor wall-time CV from prior batches ran 13–40 %. Need the full batch to confirm these aren't favorable singletons.
- The wall-time advantage over WinUI may be specific to this iter / model. The token ratio (1.36×) is the more stable claim.
- The remaining 36 % token gap is mostly the agent's
reactor.api.txtripgrep lookups (~12 calls @ ~3 K each in this run) — WinUI needs ~zero signature confirmations because XAML/MVVM is saturated in training data. Open lever for follow-on work: a structuredmur api lookup <symbol>tool, or richer always-loaded snippets for the most-grepped patterns.
Full per-iter table + variance stats will follow once the 5x batch completes.
Summary
plugins/reactor/{plugin.json, agents/, skills/<name>/SKILL.md}) so the SDK's native skill-loading subsystem firesskill.invokedinstead of agents treating SKILL.md as an ordinary file. Eval feedback flagged this as the biggest single lever (~−28% tokens, −48% cost on the win-dev-skills calculator comparison).mur pack-localpacks the framework and project-templates into<repo>/local-nupkgs/, so selfhost devs and NuGet consumers use the same#:package Microsoft.UI.Reactor@0.0.0-localreference anddotnet new reactorappresolves through the same feed. Repo-levelnuget.configwires it up.agentkit/, so consumers get the same context as selfhost viadotnet restore.mur checkfor one-line build diagnostics with skill-file pointers, and theMissingWithKeyAnalyzer(REACTOR_DSL_001) — the highest-cited gotcha the compiler doesn't catch.What's in
plugins/reactor/reactor-getting-started#:packagestarter,.csprojtemplate, hooks table, common factories, React→Reactor mapping, theme tokens, critical gotchas, mode detection (selfhost vs. consumer)reactor-dslreferences/reactor.api.txt— the full alphabetized signatures index (~12K tokens)reactor-build-and-checkmur checkusage, common-build-errors cheat table mappingREACTOR_*/CS*IDs to one-line fixesreactor-{async,design,forms,navigation,input,charts,commanding,devtools}skills/<name>.mdwith plugin-format frontmatterreactor-recipesreferences/Loading order is preload-light: only
reactor-getting-startedis preloaded, and the rest fire on demand from frontmatterdescriptionkeyword match. The legacy single-fileSKILL.md+skills/tree are preserved for environments that don't support plugin loading.Commits in this PR
e983eecac7c522SignaturesGenAfterBuild exec when target arch ≠ host arch (fixesMSB3073exit 216 on the x64 GitHub runner)af627393859e08WrapPanel→WrapGrid, recipe index wording,Pendingmention,--regen-apiPlatform, dual-write generator)d328411mur pack-localalso packs the project-templates nupkg intolocal-nupkgs/and printsdotnet new install/reactorappcommands4b51ceareactor-getting-started, slimreactor-dslto a pointer at the api indexdotnet new reactorappflow (new)For evals or workspaces outside the clone, drop a workspace-local
nuget.configwith an absolute path to<repo>/local-nupkgs/(the in-reponuget.configuses a relative path that only resolves when CWD is inside the clone).Eval-harness notes
plugins/reactor/(or<package-cache>/agentkit/plugins/reactor/).skill.invokedevents — with this layout, expect ≥2 per run.muris not yet shipped as a globaldotnet tool— lives at<repo>/bin/<arch>/mur.exeafter buildingReactor.Cli. Skill guidance prefers the loaded plugin overmur --skill/ cache-map file reads (those are only fallbacks).Test plan
dotnet build Reactor.slnx --configuration Release(CI build-solution job — green)dotnet test tests/Reactor.Tests(unit tests — green)dotnet test tests/Reactor.SelfTests(selftests — green)mur pack-localproduces bothMicrosoft.UI.Reactor.0.0.0-local.nupkgandMicrosoft.UI.Reactor.ProjectTemplates.0.0.0-local.nupkgwith the fullagentkit/plugins/reactor/treedotnet new installof the templates nupkg +dotnet new reactorapp -n MyApprestores cleanly through the local feedmur check <path>emitsREACTOR_DSL_001diagnostic with skill pointer when.WithKeyis missing